# ommbot/irc.py
#
#

""" irc handling class """

__copyright__ = 'this file is in the public domain'

from ommbot.world import rlog
import ommbot.generic
import ommbot.ircevent
import ommbot.thr
import ommbot.less
import threading
import socket
import time
import types

class Irc(object):

    """ the irc class, provides interface to irc related stuff """

    def __init__(self, name='main'):
        self.name = name
        self.nick = "ommbot"
        self.orignick = self.nick
        self.server = "localhost"
        self.port = 6667
        self.password = None
        self.ipv6 = None
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.fsock = self.sock.makefile("r")
        self.sock.settimeout(10)
        self.connected = threading.Event()
        self.stop = False
        self.reconnectcount = 0
        self.less = ommbot.less.Less(5)
        self.pongcheck = 0

    def shutdown(self):
        pass

    def connect(self, nick, server, port=6667, password=None, ipv6=None, \
reconnect=True):
        """ connect to server/port using nick """
        self.sock.settimeout(15)
        try:
            return self.doconnect(nick, server, port, password, ipv6)
        except Exception, ex:
            if self.stop:
                return 0
            rlog(10, self.name, str(ex))
            if reconnect:
                return self.reconnect()

    def doconnect(self, nick, server, port=6667, password=None, ipv6=None):
        self.nick = nick
        self.orignick = nick
        self.server = server
        self.port = port
        rlog(10, self.name, 'connecting to %s' % server)
        self.sock.connect((self.server, int(self.port)))
        # we are connected
        self.sock.settimeout(301)
        rlog(10, self.name, 'connected .. doing register')
        self.send("NICK %s" % self.nick)
        self.send("USER %s localhost localhost :%s" % (self.nick, self.nick))
        ommbot.thr.start_new_thread(self.readloop, ())

    def readloop(self):
        """ loop on the socketfile """
        self.stop = 0
        doreconnect = 0
        timeout = 1
        while not self.stop:
            try:
                res = self.fsock.readline()
                # if res == "" the other side has disconnected
                if not res and not self.stop:
                    doreconnect = 1
                    break
                if self.stop:
                    break 
                res = res.strip()
                rlog(1, self.name, res)
                # parse txt read into an ircevent
                try:
                    ievent = ommbot.ircevent.Ircevent().parse(self, res)
                except Exception, ex:
                    ommbot.generic.handle_exception()
                    continue
                # if succesfull handle the ircevent
                self.handle_ievent(ievent)
                timeout = 1
            except socket.timeout:
                if self.stop:
                    break
                timeout += 1
                if timeout > 2:
                    doreconnect = 1
                    rlog(10, self.name, 'no pong received')
                    break
                rlog(1, self.name, "socket timeout")
                self.ping()
                continue
            except Exception, ex:
                if self.stop:
                    break
                rlog(10, self.name, "error in readloop: %s" % str(ex))
                doreconnect = 1
                break
        rlog(10, self.name, 'readloop stopped')
        # see if we need to reconnect
        if doreconnect:
            self.reconnect()

    def reconnect(self):
        """ reconnect to the irc server """
        rlog(10, self.name, 'reconnecting')
        # determine how many seconds to sleep
        if self.reconnectcount > 0:
            reconsleep = self.reconnectcount*60
            rlog(10, self.name, 'sleeping %s seconds' % reconsleep)
            time.sleep(reconsleep)
        self.reconnectcount += 1
        return self.connect(self.orignick, self.server, self.port, \
                                self.password, self.ipv6)

    def send(self, txt):
        """ send text to irc server """
        if self.stop:
            return
        rlog(1, self.name + '.send', txt)
        self.sock.send(txt[:510] + '\n')

    def privmsg(self, printto, what):
        """ send privmsg to irc server """
        self.send('PRIVMSG %s :%s' % (printto, what))

    def say(self, printto, txt, fromm=None):
        """ say txt to printto """
        txt = ommbot.generic.ircdecode(txt)
        txtlist = []
        start = 0
        end = 375
        length = len(txt)
        for i in range(length/end+1):
            endword = txt.find(' ', end)
            if endword == -1:
                endword = end
            txtlist.append(txt[start:endword])
            start = endword
            end = start + 375
        size = 0
        if len(txtlist) > 1:
            if not fromm:
                self.less.add(printto, txtlist)
            else:
                self.less.add(fromm, txtlist)
            size = len(txtlist) - 1
            result = txtlist[:1][0]
            if size:
                result += " (+%s)" % size
        else:
            result = txtlist[0]
        self.privmsg(printto, result)

    def join(self, channel, password=None):
        """ join channel with optional password """
        if password:
            self.send('JOIN %s %s' % (channel, password))
        else:
            self.send('JOIN %s' % channel)

    def part(self, channel):
        """ leave channel """
        self.send('PART %s' % channel)

    def who(self, who):
        """ send who query """
        self.send('WHO %s' % who.strip())

    def names(self, channel):
        """ send names query """
        self.send('NAMES %s' % channel)

    def whois(self, who):
        """ send whois query """
        self.send('WHOIS %s' % who)

    def quit(self, reason):
        """ send quit message """
        rlog(10, self.name, 'sending quit')
        try:
            self.sock.send('QUIT :%s\n' % reason)
        except:
            pass

    def notice(self, printto, what):
        """ send notice """
        self.send('NOTICE %s :%s' % (printto, what))

    def ctcp(self, printto, what):
        """ send ctcp privmsg """
        self.send("PRIVMSG %s :\001%s\001" % (printto, what))

    def ctcpreply(self, printto, what):
        """ send ctcp notice """
        self.send("NOTICE %s :\001%s\001" % (printto, what))

    def action(self, printto, what):
        """ do action """
        self.send("PRIVMSG %s :\001ACTION %s\001" % (printto, what))

    def setnick(self, nick):
        """ set nick """
        self.orignick = nick
        self.nick = nick
        self.send('NICK %s' % nick)

    def changenick(self, nick):
        """ change nick """
        self.nick = nick
        self.send('NICK %s' % nick)

    def ping(self):
        """ ping the irc server """
        rlog(1, self.name, 'sending ping')
        try:
            self.sock.send('PING :%s\n' % self.server)
            return 1
        except Exception, ex:
            rlog(10, self.name, "can't send ping: %s" % str(ex))
            return 0

    def fakein(self, txt):
        """ do a fake ircevent """
        rlog(1, self.name + '.fakein', txt)
        self.handle_ievent(ommbot.ircevent.Ircevent().parse(self, txt))

    def handle_ievent(self, ievent):
        try:
            method = getattr(self, 'handle_%s' % ievent.cmnd.lower())
            method(ievent)
        except AttributeError:
            pass
        except Exception, ex:
            ommbot.generic.handle_exception()

    def handle_001(self, ievent):
        self.connected.set()
        rlog(10, self.name, 'logged on!')

    def handle_ping(self, ievent):
        self.send('PONG :%s' % ievent.txt)

    def handle_pong(self, ievent):
        """ set pongcheck on received pong """
        rlog(1, self.name, 'received server pong')
        self.pongcheck = 1

    def handle_notice(self, ievent):
        """ handle notice event """
        if ievent.txt and ievent.txt.find('VERSION') != -1:
            self.say(ievent.nick, config['version'], None, 'notice')
            return 1

    def handle_ctcp(self, ievent):
        """ handle client to client request """
        if ievent.txt.find('VERSION') != -1:
            self.ctcpreply(ievent.nick, 'VERSION %s' % config['version'])
            return
        if ievent.txt.find('PING') != -1:
            try:
                pingtime = ievent.txt.split()[1]
                pingtime2 = ievent.txt.split()[2]
                self.ctcpreply(ievent.nick, 'PING ' + pingtime + ' ' + \
                               pingtime2)
            except:
                pass

    def handle_error(self, ievent):
        """ show error """
        if ievent.txt.startswith('Closing'):
            rlog(0, self.name, ievent.txt)
        else:
            rlog(100, self.name + '.ERROR', ievent.txt)
